🔎 Tool 接上了,不代表事情就做得完。Tool 後面的 Environment 還要讓 Agent 找得到做法、真的跑得起來,最後也拿得到可以檢查的結果。
昨天在 Day 2|換了模型還是不穩:沿著 Agent Loop 找真正的瓶頸,我們用行事曆 Agent 的例子看了一件事:Agent 做不好,不一定是模型不夠聰明,得沿著 Agent Loop 找是哪一步出了問題。最後還提了一個 Coding Agent 的情境:它讀到舊文件,測試因為缺套件提早退出,最後把一個 passed 當成整體成功。
這三個問題有個共同點:工具都在,也都跑了。讀檔工具讀到了檔案,只是那份是舊的;執行指令的工具跑了,只是套件沒裝;測試結果也回來了,只是對不回這次的修改。工具本身沒事,出問題的是它碰到的東西。
在公司用 Claude Code 也碰過同一種狀況。它能讀檔、改程式、執行指令,官方文件也直接把它稱為模型外面的 Harness:模型提出下一步,Claude Code 執行 Tool、把結果帶回來,再讓模型繼續判斷,也就是 Day 1 講的那個 Loop。可是它改完 code、編好之後,要把東西放到 NAS 上換 code,這一段以前都是人手動做。工具它都有,卡的是它不知道這個流程怎麼走:放哪裡、換哪些檔、順序是什麼。後來我們把換 code 流程寫成 skill,讓它知道怎麼換,人為操作的環節少很多,省時間之外,也比較不會換錯東西把機器弄爛。換完之後怎麼驗證、看結果,還在推行中。
這一層,我叫它 Environment:Agent 真正工作的地方。對 Coding Agent 來說,程式碼、套件、正在跑的服務、測試環境、log 和測試結果都算。
但模型自己碰不到這一層。一個 LLM 做的事很單純:收一段文字,吐一段文字,沒有手也沒有眼睛。Tool 是碰這些東西的入口,真正去碰的是 Harness:它執行模型要的動作,再把結果寫成文字塞回下一輪。模型對環境的全部認識,就是那段文字。 所以文件找不到、套件沒裝好、做完拿不到結果,最後都會變成同一個問題:這一輪模型看到的東西,夠不夠它決定下一步。
所以今天要看的是:一個能讓 Agent 把事做完的 Environment,要準備哪些東西? 我把它分成三個時間點:動手前要找得到需要的資料和做法;動手時要真的跑得起來;做完後要拿得到能檢查結果的證據。
先用一張假設 ticket,把問題拆開來看:
付款服務偶爾回傳 HTTP 500,請找出原因並修正。
Agent 有搜尋程式碼、修改檔案和執行指令的工具,改了幾行後回覆:「已修改,測試應該會過。」但點開紀錄才發現,文件沒寫怎麼啟動服務,測試缺一個內部套件,log 又放在它讀不到的系統。程式改了,卻沒有足夠條件確認修正是否有效。
下面以這個假設情境為主,中間穿插其他 Agent 的例子,主要目的就是說明 Environment 是甚麼,跟 Harness 之間的關係又怎麼解釋。

圖:Agent 有搜尋、修改和執行的工具,卻查不到啟動說明、跑不動測試、看不到 log,最後只能回一句「應該會過」。
這類問題難抓,是因為模型看不到的部分,常常會用猜的補上。開頭那句「測試應該會過」就是這樣來的:它看到的東西不夠下結論,就補一個看起來合理的。所以環境沒準備好,常常不會直接報錯,只會表現成 Agent 做得很差。Cursor 的 Cloud Agent 文章有一個很貼近的觀察:本機 Agent 可以沿用開發者已經準備好的環境,移到雲端卻得重新建立。缺少必要條件時,不一定會直接 crash,有時只是產出品質變差,最後被誤認成模型的問題。
SWE-agent 的論文從另一個方向說同一件事:同一個模型,只改 Agent 和電腦之間的介面,建檔、翻 repository、跑測試的能力就差很多。模型外面的東西會決定結果,但出事時最先被怪的是模型。
所以 Harness 把工具接上之後,還有一件事:出狀況的時候,要分清楚是線沒接上,也就是工具後面的東西沒準備好,還是任務本身做失敗了。前者要補環境,後者要改程式,接下來要做的事完全不同。
Day 1 講的 Tool Call,其實是模型在輸出裡寫「我要呼叫這個工具、參數是這些」,Harness 看到了才去執行,再把結果寫成文字,塞進下一輪的輸入。模型從頭到尾只在讀字、寫字。
所以 Harness 管的其實就兩件事:每一輪模型看得到什麼,和看完之後它比較可能往哪走。第一件是挑。開頭的 system prompt 和工具說明通常整場不動,每一輪真正在變的是尾巴新進來的東西:使用者的話、工具結果。新東西放尾巴、放多少、怎麼寫,是這裡在管的。第二件是引導:同一個工具結果,寫法不同,模型的下一步就不同。
通道只有這一條。Harness 想引導模型做什麼,最直接的方法就是決定 Tool Result 長什麼樣。模型的下一步,很大一部分是被上一個 Tool Result 決定的。
例如模型要讀一個很大的 log。整個塞回去,這一輪 context 就爆了,而且之後每一輪都得帶著這坨東西。截掉一半、只留最後幾行,模型不知道前面有什麼,只能拿那幾行硬猜;Day 2 那個把 passed 當成整體成功的例子就是這樣來的。比較好的回法是內容不塞回去,回檔案路徑加一句提示,像「檔案太大,已存到 artifacts/test.log,請用 grep 或 tail 分段讀」(示意,不是固定格式)。模型看到這句,下一步就去 grep,不用猜。Anthropic 的 Context engineering 文章講的就是這種做法:平常只留檔案路徑、查詢語句這類輕量的參照,需要時再用工具把資料載進來。Anthropic 的工具設計文章也提醒,截斷了就要附一句指引,別只留一截讓模型猜後面是什麼。

圖:同一個大 log,三種回法。整份塞回去撐爆 context,截到剩幾行讓模型猜,回路徑加一句提示讓它自己去 grep。行數和提示文字是示意。
出錯的時候更是這樣。工具設計那篇講得很直接:工具出錯時,可以把錯誤回應當 prompt 來寫,講清楚具體、可行的下一步,別丟回不透明的錯誤碼或 traceback。用 Claude Code 就常碰到:Edit 工具在沒先讀過檔案時會拒絕執行,回的訊息直接說要先 Read,模型下一步就去讀。同一個失敗,回 command failed 和回「套件下載失敗,測試沒開始」,模型的下一步會完全不同。
引導和替模型下結論是兩件事。Harness 分得出「沒跑成」和「跑了但失敗」就該講;分不出的原因,就保留未知。

圖:模型碰不到 sandbox,只看得到 Harness 整理出來的 Tool Result。同一個失敗,回 command failed 和回「套件下載失敗,測試沒開始」,模型的下一步不一樣。這是示意,不是實際架構。
回到付款那張 ticket。Agent 從接到任務到回報,有三個時候要靠 Tool Result 才走得下去,每次要的東西不一樣。動手前,它要知道這個專案怎麼跑:建置、測試指令在哪份文件。動手時,它要知道剛才那條指令到底跑了沒。做完後,它要知道那個綠燈是不是這次修改的。三個時刻要的東西不同,少了之後模型看到的也不同,Harness 要補的自然不一樣。所以我照這三個時刻拆開來看。
下面的表每一格填的都是付款 ticket 裡的東西:這一輪 context 裡該有什麼、少了模型看到的是什麼、Harness 要做什麼。
| 時刻 | 這一輪 Context 裡要有什麼 | 少了,模型看到的是 | Harness 要做的 |
|---|---|---|---|
| 動手前 | 這個專案的操作入口:建置、換版、測試指令在哪份文件,適用哪個版本 | 一份可能過期的 README,或整個 repo 的搜尋結果 | 給一個入口,標清楚版本和適用範圍 |
| 動手時 | 執行結果,而且分得出「沒跑成」和「跑了但失敗」,卡在哪一步 | 一行 command failed,或被截到只剩最後幾行的輸出 |
「做不成」和「做失敗」分開回報 |
| 做完後 | 測了哪份修改、測試範圍、跳過了什麼、原始報告在哪 | 一個綠燈,或一個 passed |
結果綁這次的修改和檢查範圍,跳過不算通過 |
這張表是我自己整理的檢查方式,coding 以外的 Agent 也適用。拿 Day 2 的 Calendar 助理來對:動手前,它要知道哪個帳號的行事曆算正式的;動手時,API 憑證、配額和時區設定要在;做完後,要拿得到 API 回應和實際排進去的紀錄,不是一句「已排好」。τ-bench 評零售和航空客服 Agent 就是這樣做:給 Agent 一組 API 工具、政策文件和資料庫,最後比對資料庫的狀態有沒有變成該有的樣子,不看對話裡說了什麼。
不用把所有資料和權限都開給 Agent,在允許的範圍內,有這項任務需要的條件就夠了;哪些事允許做,是另一個題目。
下面照動手前、動手時、做完後的順序,一個一個說明。
想讓 Claude Code 接著換版、跑驗證,先要讓它找得到這個專案的操作方式。建置要下什麼指令?換版要跑哪支 script?測試需要哪些設定?如果答案只在某位同事的記憶裡,或放在它讀不到的聊天紀錄中,光有搜尋和讀檔工具還不夠。
找到了,也要看是不是這次該用的那份。假設 README 寫一套測試方法,另一份文件卻寫另一套,就要交代各自適用哪個版本、哪個環境。不是挑日期最新的就好;這次如果在修舊版分支,可能反而要看舊版的說明。
所以「我們有寫文件」還不夠,Agent 要知道去哪裡查、真的讀得到,也知道哪份適用這次任務。
OpenAI 的 Harness engineering 案例把 AGENTS.md 寫成約一百行的目錄,詳細說明放在 repository 的 docs/,再用 lint、CI 和維護工作檢查連結與文件狀況。值得學的地方在於先給一份簡短的指引,讓 Agent 需要細節時知道往哪裡找;行數多少沒那麼重要。
以前面的付款 ticket 為例,可以先提供這樣的指引。下面只是示意路徑,不是真實專案的檔案:
服務:payment
建置、換版與測試:docs/payment/development.md
需要的套件、設定與權限:docs/payment/environment.md
問題排查與 log 位置:docs/payment/troubleshooting.md
點進去的文件,再寫清楚實際指令、操作步驟、適用版本,以及由誰維護。文件放在 repository 或內部文件站都可以,重點是 Agent 使用的帳號真的讀得到。不用把全部內容塞進 prompt,也不用讓它每次都從整個專案重新找起。入口短、整場不變,可以固定放在開頭;細節等模型需要時再用工具讀,只進那一輪。
用 Claude Code 的人對這個模式應該不陌生:Skill。一個 skill 就是一個資料夾加一份 SKILL.md。官方文件寫得很直接:session 開始時模型只看得到每個 skill 的說明(description),整份 SKILL.md 等被叫到才進 context;更大的參考資料放同資料夾的其他檔案,需要時才讀。它也建議 SKILL.md 控制在 500 行內,細節搬去別的檔案。上面那份付款入口就是同一個做法:入口只列服務、換版、驗證各一行,細節放檔案裡,模型需要時再讀。
文件更新時,也要標明舊方法是否還適用。自動檢查可以發現連結失效,卻不能保證裡面的指令仍然正確;內容還是需要維護。找不到說明、或兩份文件互相衝突時,就該把缺口說出來,別默默挑一份照做。
這件事也不只出現在寫程式。資料分析 Agent 就算能查資料庫,還是要知道欄位代表什麼、指標怎麼算、哪些資料表可以接在一起。Vercel 的 d0 案例把不少專用工具拿掉,改讓 Agent 在 sandbox 裡讀取 YAML、Markdown 和 JSON,再產生 SQL。這些檔案記的就是前面說的欄位意義、指標算法這些資料定義,他們稱為 semantic layer。
文章標題強調拿掉了 80% 的工具,但這裡要看的是:Agent 拿到的除了讀檔工具,還有原本就整理好的資料說明。 Vercel 也提醒,定義混亂、資料表關聯沒寫清楚,開放讀檔並不會自動解決問題。
這和 Day 2 講的分工並不衝突:固定步驟可以交給工具,需要探索時也可以讓 Agent 自己查資料。新版仍保留 ExecuteSQL 執行查詢,不是讓模型口算。他們的五題測試從完成四題變成五題,速度約快 3.5 倍、token 用量也下降,但這是當時模型、資料和設定下的結果,不能推成「刪工具就會變好」。工具變少,也不代表讀檔、執行指令或查資料庫的權限就變小了。
回到開頭的換版與驗證,這一節先確認的是:Agent 能不能自己拿到這個專案的操作說明,還是得等人一句一句告訴它。 文件和資料是專案自己維護的,Harness 負責讓模型讀得到、把讀到的內容送進這一輪;至於照著說明能不能真的跑起來,是下一節的事。
找到測試指令之後,下一個問題是:它真的跑得動嗎?
README 寫著 run tests,在你的電腦上跑得起來,不代表在 Agent 的執行環境裡也跑得起來。原本的電腦可能早就下載過內部套件、啟動了背景服務、登入過帳號,還有手動改過卻沒寫進文件的設定。如果 Agent 是在另外建立的 sandbox 裡執行,就不能假設這些東西會自動跟過去。
Shopify 的 River 文章提到,他們把程式碼集中到 monorepo,再用 Nix 建立可重現的開發環境、CI 和正式環境映像。原本用來改善開發流程的基礎建設,也讓 Agent 比較容易找到程式碼、重建需要的環境。
不必因此跟著換 monorepo 或 Nix。這裡要確認的是:同一份程式碼,換到這次任務的環境,能不能照著寫下來的步驟準備好,不用靠某位工程師記得少了什麼。
可以沿用現有的 bootstrap(環境準備流程),留下這次用的程式碼版本、環境映像或 lockfile 版本,還有準備有沒有成功。cache 可以加速,但不能只因為某台舊機器剛好有套件,就以為新的環境也一定建得起來。
前面的付款 ticket 就可能卡在這裡。假設安裝紀錄已確認必要的內部套件下載失敗,工具回傳可以整理成下面這樣;這只是說明用的格式:
{
"phase": "setup",
"status": "failed",
"exit_code": 1,
"tests_ran": False,
"reason": "必要的內部套件下載失敗",
"output_ref": "artifacts/setup.log",
}
我不是說每個系統都要新增這幾個欄位。要讓模型分得出來的是:測試沒有開始,和測試跑完但不通過,是兩件事。 只收到 command failed,就很難判斷該修程式、補環境,還是回報目前被擋住。
整合測試需要的服務沒啟動、必要設定缺失,也要先說清楚卡在哪一步。原因不明就保留未知,不能只看 exit code 猜原因,更不能讓環境問題一路被當成業務程式的錯誤。
換成打 API 的 Agent,卡的地方變成憑證過期、配額用完、測試環境裡沒有那個帳號。要分的還是同一件事:動作根本沒做成,和動作做了但結果不對,不能混在一起回報。
假設套件補齊、服務也啟動了,Agent 修改後跑了測試,畫面終於變綠。這樣就能說那張 HTTP 500 ticket 處理完了嗎?
還要看測了什麼。只跑不相關的單元測試,不能證明付款流程已恢復;只看程式碼,也不等於真的重現過錯誤。昨天談的是留下 Agent 做過什麼的紀錄,今天則要讓工作環境真的能交出可以拿來檢查的結果。
OpenAI 的案例讓每個 git worktree,也就是同一程式庫的不同工作目錄,都能啟動各自的應用。Codex 可以操作瀏覽器、取得 DOM 和截圖,也能查詢這個環境的 log、metrics 和 trace。metrics 是延遲、錯誤數等量測資料,trace 則能追蹤一次 request 經過的步驟。
不需要每項任務都接上整套觀測系統。付款錯誤可以先取得重現步驟、相關測試和執行紀錄;UI 修改可能需要看畫面,資料處理則可能要查實際筆數。只在 prompt 多寫一句「請確實驗證」還不夠。 環境也要提供能執行的檢查,以及能拿來判斷結果的依據。
Claude Code 的官方建議也強調,要給它能自行執行、讀取結果的檢查,讓它改完後能根據結果繼續修正,不用每次都等人發現問題。Anthropic 的 Building effective agents則把工具結果、程式執行結果等環境回饋稱為 ground truth,讓 Agent 有依據判斷進度。重點在把檢查結果送回下一輪,光最後多交一份報告不夠。
整個過程可以這樣看:
讀取現況 → 修改 → 必要的建置/換版 → 執行檢查 → 取得證據 → 決定再修、結束或交給人處理
不過,拿到結果也不能不看條件就信。至少還要問:測的是哪份修改?指定的檢查真的跑了嗎?
GitHub 的 required checks 文件有個具體例子:job 因條件不符被跳過,可以回報 Success;整個 workflow 被路徑篩選跳過,相關檢查則可能停在 Pending。required checks 也要對應最新的 commit,舊版的綠燈不算。檢查狀態顯示成功,不等於每項測試都執行過。
所以測試報告要能對回實際受測的程式。如果 Agent 的修改還沒 commit,只記起始 commit 就不夠,還要有 patch、檔案樹或建置產物這類能認出這份修改的東西。再保留檢查時間、實際測試範圍、通過或跳過,以及原始報告的位置,才知道這個「通過」能支持什麼結論。
我會把這兩件事分開看:環境讓我們有辦法取得證據,驗收條件則決定這些證據能支持什麼結論。 以前面的假設 ticket 來說,服務能啟動,不代表付款流程正確;HTTP 200,也還要核對該寫入的交易有沒有出現。如果問題只在特定資料或併發下才出現,簡化環境裡成功一次還不夠。
Anthropic 的長時間 Agent 實驗有個具體例子:在開發 Web App 時,Claude 即使跑了單元測試或用 curl 呼叫服務,仍可能沒發現功能無法端到端運作。加入瀏覽器工具並明確要求實際操作後,驗證有所改善,但文章也指出工具與視覺能力仍有盲點。
我不是說每個任務都要跑完整的端到端測試,重點是檢查要對得上想確認的行為。比起一句「驗證完成」,我更希望看到:這次在哪個環境、確認了哪些條件,還有哪些沒有驗證。 結果可以帶回 Agent,讓它繼續修正;但測試通過的範圍,不能直接擴大成「整個功能都沒問題」。

圖:三個時刻各自要什麼進 context、少了模型看到什麼、Harness 要做什麼。環境沒準備好和任務失敗是兩件事,Harness 要分得出來。這是示意,不用照搬成部署架構。
要替 Agent 準備的東西看起來不少,不過環境建置、套件管理、測試和報告,本來就可以交給既有工具。Harness 要做的是把入口接好、在允許的範圍內安排執行,再把結果帶回來,不用另外重寫 CI 或測試框架。OpenAI 的案例也直接使用既有開發工具。
只有一個程式庫時,可以先整理一份短入口文件、一個環境準備指令,以及能產出報告的標準測試方式。服務變多,再補服務目錄、負責人和依賴關係。複雜環境可以用 image 或 snapshot 加速建立,但仍要記清楚版本,還有哪些設定是在啟動時補上的。
要知道三個時刻該有的東西齊不齊,有一個很直接的方法:換到乾淨的任務環境,用 Agent 實際使用的身分和權限,照著入口走一次。 能不能取得依賴、啟動服務、執行
指定測試,再拿回對應版本的報告?如果還需要開發者手動補一個未記錄的步驟,那就是待補的缺口。
這個檢查不需要把開發者的憑證或整個內網交給 Agent。敏感資料仍透過受控查詢、遮罩和存取限制處理;太大的報告一樣是留原始檔位置,給摘要,或讓它分段讀。
後續可以觀察環境準備失敗的原因、同版本能否重新建立,以及取得第一份有效驗證結果花多久。這些數字用來改善工作條件,和模型在任務上的正確率分開看。
回到開頭換版的例子。換版已經交給 Claude Code 了,如果驗證也能交給它,再讓它幫忙做一輪 code review,人剩下要接的是什麼?
公開案例其實沒有同一種答案。Anthropic 的 Code Review 產品介紹把找問題交給 Agent,但保留人工核准 PR;OpenAI 的 Harness engineering 實驗則在從零建立產品的特定專案裡,逐步把多數 review 交給 Agent,不一定要人工 review。這些是各自的流程選擇,不能直接推成所有專案的標準答案。
Google 的 code review 指南提醒,review 不只找 bug,也要看設計是否合適、複雜度是否必要,以及測試本身是否正確、有用。我自己現在的做法偏向看行為和測項:Claude Code 改完,我先看它改出來的行為對不對、測項有沒有測到那個行為,再確認測項跑出來的結果和它回報的一致,這些都對得上,才往 CI 推。這只是我目前的做法。大家都還在摸索,不同的分工各有各的好處,比較確定的只有一件事:人的角色會離一行一行 review 越來越遠。
但這裡還有幾個沒有定論的問題:
這些問題先留著。對今天的主題來說,至少要讓人和 Agent 都能拿到對應的修改、可檢查的結果,以及還沒驗證的部分,別只收到一句「我看過了,沒問題」。
回到那張 HTTP 500 ticket,可以試著問自己:Agent 有執行指令的工具,卻下載不到必要套件,這算任務失敗嗎?測試顯示通過,但跑的是修改前的程式,能證明這次修正有效嗎?
第一題代表任務還沒完成,但不能因此說程式改錯了;該回報的是「環境受阻,測試尚未執行」,不是「測試已執行但不通過」。第二題不能,做完後的結果沒對回這次修改,那個綠燈證明不了這次修正有效。
Harness 要在動手前、動手時、做完後各確認一件事:找得到該讀的、做得成、看得到對得回這次修改的結果。環境沒準備好就講清楚,不能讓模型猜。 這些條件準備好,不代表模型每次都會成功,但至少出錯時知道該補環境還是改程式。
資料怎麼找、工具怎麼設計、哪些事允許做、任務算不算真的完成,後面各有專門的天數。今天只確認環境在三個時刻該準備的東西都準備好了。
接下來還有另一個問題:任務跑到一半要等人核准,或執行的 worker 重啟了,系統怎麼知道現在做到哪裡?
SKILL.md 被叫到才進 context,大的參考資料放支援檔案;用來說明「短入口固定放開頭、細節需要時再讀」的做法。curl 未涵蓋端到端使用流程的問題,以及加入瀏覽器驗證後的改善與盲點;不是要求所有任務都使用瀏覽器。